// axios请求函数
import axios from 'axios';

import type {
  AxiosInstance,
  AxiosRequestConfig,
  AxiosResponse,
  InternalAxiosRequestConfig,
  AxiosError,
} from 'axios';

type Result<T> = {
  code: number;
  message: string;
  result: T;
};

// 导出Request类，可以用来自定义传递配置来创建实例
export class Request {
  // axios 实例
  instance: AxiosInstance;
  // 基础配置，url和超时时间
  baseConfig: AxiosRequestConfig = {
    baseURL: '/hotel', //请求后台地址模块
    timeout: 60000,
    headers: {
      'X-Requested-With': 'XMLHttpRequest',
    },
  };

  constructor(config: AxiosRequestConfig) {
    // 使用axios.create创建axios实例
    this.instance = axios.create(Object.assign(this.baseConfig, config));

    // 请求拦截器
    this.instance.interceptors.request.use(
      (config: InternalAxiosRequestConfig) => {
        // 一般会请求拦截里面加token，用于后端的验证
        const token = localStorage.getItem('token') as string;
        if (token) {
          config.headers!.Authorization = token;
        }

        return config;
      },
      (err: unknown) => {
        // 请求错误，这里可以用全局提示框进行提示
        return Promise.reject(err);
      }
    );

    // 返回结果的相应拦截器
    this.instance.interceptors.response.use(
      (res: AxiosResponse) => {
        // 直接返回res，当然你也可以只返回res.data
        // 系统如果有自定义code也可以在这里处理
        return res.data;
      },
      (err: AxiosError) => {
        const { response } = err;

        // 这里用来处理http常见错误，进行全局提示
        if (response) {
          let message = '';

          switch (response.status) {
            case 400:
              message = '请求错误(400)';
              break;
            case 401:
              message = '未授权，请重新登录(401)';
              // 这里可以做清空storage并跳转到登录页的操作
              break;
            case 403:
              message = '拒绝访问(403)';
              break;
            case 404:
              message = '请求出错(404)';
              break;
            case 408:
              message = '请求超时(408)';
              break;
            case 500:
              message = '服务器错误(500)';
              break;
            case 501:
              message = '服务未实现(501)';
              break;
            case 502:
              message = '网络错误(502)';
              break;
            case 503:
              message = '服务不可用(503)';
              break;
            case 504:
              message = '网络超时(504)';
              break;
            case 505:
              message = 'HTTP版本不受支持(505)';
              break;
            default:
              message = `连接出错(${response.status})!`;
          }

          console.error(message);
        }
        // 这里错误消息可以使用全局弹框展示出来
        // 比如element plus 可以使用 ElMessage
        // ElMessage({
        //   showClose: true,
        //   message: `${message}，请检查网络或联系管理员！`,
        //   type: "error",
        // });
        // 这里是AxiosError类型，所以一般我们只reject我们需要的响应即可
        return Promise.reject(response);
      }
    );
  }

  // 定义请求方法
  public request(config: AxiosRequestConfig): Promise<AxiosResponse> {
    return this.instance.request(config);
  }

  public get<T = unknown>(
    url: string,
    config?: AxiosRequestConfig
  ): Promise<AxiosResponse<Result<T>>> {
    return this.instance.get(url, config);
  }

  public post<T = unknown>(
    url: string,
    data?: Record<string, unknown>,
    config?: AxiosRequestConfig
  ): Promise<AxiosResponse<Result<T>>> {
    return this.instance.post(url, data, config);
  }

  public upload<T = unknown>(
    url: string,
    data?: Record<string, unknown>,
    config?: AxiosRequestConfig
  ): Promise<AxiosResponse<Result<T>>> {
    //封装表单数据对象
    const RequestData = new FormData();

    // 检查传入的 data 是否存在
    if (!data || Object.getOwnPropertyNames(data).length === 0) {
      throw new Error('No data provided for upload');
    }

    // 遍历参数并添加到 RequestData 中
    Object.entries(data).forEach(([key, value]) => {
      if (Array.isArray(value)) {
        // 如果值是数组，遍历数组中的每个元素
        value.forEach((item) => {
          if (item instanceof File) {
            // 如果是文件
            RequestData.append(`${key}[]`, item);
          } else if (typeof item === 'string') {
            // 如果是字符串或数字
            RequestData.append(`${key}[]`, item);
          } else if (typeof item === 'number') {
            // 如果是字符串或数字
            RequestData.append(`${key}[]`, item.toString());
          } else {
            throw new Error(`Unsupported array item type for key "${key}"`);
          }
        });
      } else if (value instanceof File) {
        // 如果是单个文件
        RequestData.append(key, value);
      } else if (typeof value === 'string' || typeof value === 'number') {
        // 如果是字符串或数字
        RequestData.append(key, value.toString());
      } else {
        throw new Error(`Unsupported data type for key "${key}"`);
      }
    });

    // 请求头
    config = {
      ...config,
      headers: {
        ...config?.headers,
        'Content-Type': 'multipart/form-data',
      },
    };

    return this.instance.post(url, RequestData, config);
  }

  public put<T = object>(
    url: string,
    data?: Record<string, unknown>,
    config?: AxiosRequestConfig
  ): Promise<AxiosResponse<Result<T>>> {
    return this.instance.put(url, data, config);
  }

  public delete<T = unknown>(
    url: string,
    config?: AxiosRequestConfig
  ): Promise<AxiosResponse<Result<T>>> {
    return this.instance.delete(url, config);
  }
}

// 默认导出Request实例
export default new Request({});
